《Android 应用 之路》简易贪吃蛇

最简单的贪吃蛇

最近想着忙里偷闲写点简单的Android应用,增加一些生活乐趣,由于平时工作主要精力并不是集中在书写apk上,更多的是解决代码问题和维护模块稳定,但是写代码本身是一件比较有趣的事情,因为这个过程是从无到有的。

名称:贪吃蛇
开发环境:IntelliJ IDEA 14.0.3
思路:

  1. 定时刷新界面,因为蛇是不会停止的

  2. 随机产生食物

  3. 蛇体的更新

  4. 蛇体如何出现移动的效果

    缺陷:没有结束,没有碰壁,没有追尾,除非推出(现在知道为什么是最简单的了吧)

代码分析:

  1. MyActivity, 主要作用就是定时刷新界面,让蛇不停的前进
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
private SnakeView mSnakeView;  //填充窗口的View,刷新的对象
private static final int REFRESH = 1; //定义消息 防止硬编码
private static final int REFRESHINTERVAL = 300; //刷新的时间间隔
private boolean isPaused = false; //线程的停止标志位
private Handler mHandler = new Handler() { //thread handler 消息处理
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if(msg.arg1 == REFRESH) {
if(mSnakeView != null) {
mSnakeView.invalidate();
}
}
}

};
private Thread mRefreshThread; //用于发送刷新消息的线程

//Activity的onCreate方法
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSnakeView = new SnakeView(this);
setContentView(mSnakeView);
isPaused = false;

mRefreshThread = new Thread("TimerThread"){

@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while(!isPaused) {
Message msg = mHandler.obtainMessage();
msg.arg1 = REFRESH;
mHandler.sendMessage(msg);
try {
Thread.sleep(REFRESHINTERVAL); //休眠一段时间后再发送消息刷新界面
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

};
mRefreshThread.start(); //启动线程
}
  1. SnakeView ,主游戏界面,蛇体,背景和食物的显示界面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
//自定义View
public class SnakeView extends View{
public static final String TAG = "SnakeView";

private int mWidth; //view的宽
private int mHeight; //View的高

private static final int sXOffset = 0 ;
private static final int sYOffset = 0 ; // X坐标和Y坐标的偏移量,可以修改来缩小游戏范围

private final int BOXWIDTH = 30; //食物的边长,蛇体的宽度
private Random mRandom = new Random(); //用于产生随机数
private Point mFoodPosition; //食物的位置
private boolean mIsFoodDone = true; //食物是否已经被吃掉

private ArrayList<Point> mSnakeList; //蛇体可以看做是很多食物组成的
private Paint mSnakePaint; //用于画蛇的画笔
private int mSnakeDirection = 0; //蛇体运动的方向
private final int UP = 1;
private final int DOWN = 2;
private final int LEFT = 3;
private final int RIGHT =4;

private Paint mBgPaint;//游戏背景画笔
private Paint mFoodPaint;//食物画笔


public SnakeView(Context context) {
super(context);
// TODO Auto-generated constructor stub
mSnakeList = new ArrayList<Point>();
mSnakePaint = new Paint();
mSnakePaint.setColor(Color.RED);
mSnakePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mSnakeList.add(new Point(500,500));
mSnakeList.add(new Point(500,530)); //初始化一条丑陋的蛇

mSnakeDirection = RIGHT;
mIsFoodDone = true;
mFoodPosition= new Point();

mFoodPaint = new Paint();
mFoodPaint.setColor(Color.CYAN);
mFoodPaint.setStyle(Paint.Style.FILL);

mBgPaint = new Paint();
Paint paint = new Paint();
paint.setColor(Color.WHITE); //初始化各种画笔

}

@Override
public boolean onTouchEvent(MotionEvent event) { //通过手势来改变蛇体运动方向
// TODO Auto-generated method stub

int x = (int)(event.getX());
int y = (int)(event.getY());
Log.e(TAG, "x =" + x + " y = " + y + " mSnakeDirection = " + mSnakeDirection);
if(mSnakeDirection == UP || mSnakeDirection == DOWN) {
if(x < head.x) mSnakeDirection = LEFT;
if(x > head.x) mSnakeDirection = RIGHT;
} else if(mSnakeDirection == LEFT || mSnakeDirection == RIGHT) {
if(y < head.y) mSnakeDirection = UP;
if(y > head.y) mSnakeDirection= DOWN;
}
//Log.e(TAG, "after adjust mSnakeDirection = " + mSnakeDirection);
return super.onTouchEvent(event);
}

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
//Log.e(TAG ,"onDraw");
super.onDraw(canvas);

drawBg(canvas, mBgPaint); //画背景
drawFood(canvas, mFoodPaint);//画食物
drawSnake(canvas, mSnakePaint); //画蛇

}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO Auto-generated method stub
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}

//画背景 这里通过sXOffset, sYOffset可以实现对蛇活动区域的限制
private void drawBg(Canvas canvas, Paint paint) {
canvas.drawColor(Color.WHITE);
//Rect rect = new Rect(sXOffset, sYOffset, mWidth - sXOffset, mHeight - sYOffset);
//canvas.drawRect(rect, paint);
}

//画蛇体
private void drawSnake(Canvas canvas, Paint paint) {
for(int i = 0 ; i < mSnakeList.size() ; i++ ) {
Point point = mSnakeList.get(i);
Rect rect = new Rect(point.x , point.y , point.x + BOXWIDTH , point.y + BOXWIDTH);
canvas.drawRect(rect, paint);
}
//蛇移动,更新list为下一次刷新做准备
snakeMove(mSnakeList, mSnakeDirection);
if(isFoodEaten()) { //如果吃了食物,长度加1
mIsFoodDone = true;
} else { //如果没有吃食物,由于前进时加了一个 这里删除尾巴,出现移动的效果
mSnakeList.remove(mSnakeList.size() - 1);
}
}

//画食物
private void drawFood(Canvas canvas, Paint paint) {
if(mIsFoodDone) { //只在前一个食物被吃掉的情况下才产生食物
mFoodPosition.x = mRandom.nextInt(mWidth - 2*sXOffset - BOXWIDTH) + sXOffset ;
mFoodPosition.y = mRandom.nextInt(mWidth - 2*sYOffset - BOXWIDTH) + sYOffset ;
mIsFoodDone = false;
}
Rect food = new Rect(mFoodPosition.x , mFoodPosition.y , mFoodPosition.x + BOXWIDTH , mFoodPosition.y + BOXWIDTH);
canvas.drawRect(food, paint);

}

public void snakeMove(ArrayList<Point> list , int direction) {
//Log.e(TAG," snakeMove ArrayList = " + list.toString());
Point orighead = list.get(0);
Point newhead = new Point();
//蛇前进,实现原理就是头加尾减,若吃到食物,头加尾不减
switch(direction) {
case UP:
newhead.x = orighead.x;
newhead.y = orighead.y - BOXWIDTH ;
break;
case DOWN:
newhead.x = orighead.x;
newhead.y = orighead.y + BOXWIDTH ;
break;
case LEFT:
newhead.x = orighead.x - BOXWIDTH;
newhead.y = orighead.y;
break;
case RIGHT:
newhead.x = orighead.x + BOXWIDTH ;
newhead.y = orighead.y;
break;
default:
break;
}
adjustHead(newhead);
list.add(0, newhead);
}

//边界判断
private boolean isOutBound(Point point) {
if(point.x < sXOffset || point.x > mWidth - sXOffset) return true;
if(point.y < sYOffset || point.y > mHeight - sYOffset) return true;
return false;
}

//出了边界,重新回来
private void adjustHead(Point point) {
//Log.e(TAG, "checkBound = " + isOutBound(point));
if(isOutBound(point)){
if(mSnakeDirection == UP) point.y = mHeight - sYOffset - BOXWIDTH;
if(mSnakeDirection == DOWN) point.y = sYOffset;
if(mSnakeDirection == LEFT) point.x = mWidth - sYOffset - BOXWIDTH;
if(mSnakeDirection == RIGHT) point.x = sXOffset;
}
}

//判断食物是否可以被吃
private boolean isFoodEaten() {
if(!mIsFoodDone) {
Rect foodrect = new Rect(mFoodPosition.x, mFoodPosition.y, mFoodPosition.x + BOXWIDTH, mFoodPosition.y + BOXWIDTH);
Point head = mSnakeList.get(0);
Rect headrect = new Rect(head.x, head.y, head.x + BOXWIDTH , head.y + BOXWIDTH);
return foodrect.intersect(headrect);
}
return false;
}

}

效果展示:
(制作Gif有点不流畅实际效果很流畅)

这里写图片描述

代码附件:

http://download.csdn.net/detail/poorkick/9497370

欢迎讨论!

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×